import asyncHandler from "express-async-handler";
import { body, validationResult } from "express-validator";

import Book from "../models/book";
import Author from "../models/author";
import Genre from "../models/genre";
import BookInstance from "../models/bookinstance";

export const index = asyncHandler(async (req, res, next) => {
  const [
    numBooks,
    numBookInastances,
    numAvailableBookInstances,
    numAuthors,
    numGenres,
  ] = await Promise.all([
    Book.countDocuments({}).exec(),
    BookInstance.countDocuments({}).exec(),
    BookInstance.countDocuments({ status: "Available" }).exec(),
    Author.countDocuments({}).exec(),
    Genre.countDocuments({}).exec(),
  ]);

  res.render("index", {
    title: "Local Library Home",
    book_count: numBooks,
    book_instance_count: numBookInastances,
    book_instance_available_count: numAvailableBookInstances,
    author_count: numAuthors,
    genre_count: numGenres,
  });
});

// 显示所有的图书
export const book_list = asyncHandler(async (req, res, next) => {
  const allBooks = await Book.find({}, "title author")
    .sort({ title: 1 })
    .populate("author")
    .exec();
  res.render("book_list", { title: "Book List", book_list: allBooks });
});

// 显示特定图书的详情页面。
export const book_detail = asyncHandler(async (req, res, next) => {
  const [book, bookInstances] = await Promise.all([
    Book.findById(req.params.id).populate("author").populate("genre").exec(),
    BookInstance.find({ book: req.params.id }).exec(),
  ]);
  if (book === null) {
    const err = new Error("Book not found");
    err.status = 404;
    return next(err);
  }
  res.render("book_detail", {
    title: book.title,
    book: book,
    book_instances: bookInstances,
  });
});

// 通过 GET 显示创建图书。
export const book_create_get = asyncHandler(async (req, res, next) => {
  // 先查找作者和类别
  const [authors, genres] = await Promise.all([
    Author.find().exec(),
    Genre.find().exec(),
  ]);
  res.render("book_form", {
    title: "Create Book",
    authors,
    genres,
  });
});

// 以 POST 方式处理创建图书。
export const book_create_post = [
  // 将类别转换为数组
  (req, res, next) => {
    if (!req.body.genre instanceof Array) {
      if (typeof req.body.genre === "undefined") req.body.genre = [];
      else req.body.genre = [req.body.genre];
    }
    next();
  },
  body("title", "Title must not be empty.").isLength({ min: 1 }).trim(),
  body("author", "Author must not be empty.").isLength({ min: 1 }).trim(),
  body("summary", "Summary must not be empty.").isLength({ min: 1 }).trim(),
  body("isbn", "ISBN must not be empty.").isLength({ min: 1 }).trim(),
  // 使用通配符清理字段
  body("*").trim().escape(),
  body("genre.*").escape(),
  asyncHandler(async (req, res, next) => {
    const errors = validationResult(req);
    const book = new Book({
      title: req.body.title,
      author: req.body.author,
      summary: req.body.summary,
      isbn: req.body.isbn,
      genre: req.body.genre,
    });

    if (!errors.isEmpty()) {
      const [authors, genres] = await Promise.all([
        Author.find().exec(),
        Genre.find().exec(),
      ]);
      // 标记创建的类别为选中状态
      for (const genre of genres) {
        if (book.genre.indexOf(genre._id) > -1) {
          genre.checked = "true";
        }
      }
      res.render("book_form", {
        title: "Create Book",
        authors,
        genres,
        book: book,
        errors: errors.array(),
      });
      return;
    } else {
      await book.save();
      res.redirect(book.url);
    }
  }),
];

// 通过 GET 显示删除图书。
export const book_delete_get = asyncHandler(async (req, res, next) => {
  const [book, bookInstances] = await Promise.all([
    Book.findById(req.params.id).populate("author").populate("genre").exec(),
    BookInstance.find({ book: req.params.id }).exec(),
  ]);
  if (book === null) {
    res.redirect("/catalog/books");
  } else {
    res.render("book_delete", {
      title: "Delete Book",
      book,
      bookInstances,
    });
  }
});

// 以 POST 方式处理删除图书。
export const book_delete_post = asyncHandler(async (req, res, next) => {
  const [book, bookInstances] = await Promise.all([
    Book.findById(req.params.id).populate("author").populate("genre").exec(),
    BookInstance.find({ book: req.params.id }).exec(),
  ]);

  if (book === null) {
    return res.redirect("/catalog/books");
  }
  if (bookInstances.length > 0) {
    return res.render("book_delete", {
      title: "Delete Book",
      book,
      bookInstances,
    });
  } else {
    await Book.findByIdAndDelete(req.params.id);
    res.redirect("/catalog/books");
  }
});

// 通过 GET 显示更新图书。
export const book_update_get = asyncHandler(async (req, res, next) => {
  const [book, genres, authors] = await Promise.all([
    Book.findById(req.params.id).populate("author").populate("genre").exec(),
    Genre.find().sort({ name: 1 }).exec(),
    Author.find().sort({ family_name: 1 }).exec(),
  ]);

  if (book === null) {
    const err = new Error("Book not found");
    err.status = 404;
    return next(err);
  }

  genres.forEach((genre) => {
    if (book.genre.some((item) => item.id === genre.id)) genre.checked = "true";
  });
  res.render("book_form", {
    title: "Update Book",
    authors: authors,
    genres: genres,
    book: book,
  });
});

// 处理 POST 时的更新图书。
export const book_update_post = [
  (req, res, next) => {
    if (!(req.body.genre instanceof Array)) {
      if (typeof req.body.genre === "undefined") req.body.genre = [];
      else req.body.genre = new Array(req.body.genre);
    }
    next();
  },

  // Validate fields.
  body("title", "Title must not be empty.").isLength({ min: 1 }).trim(),
  body("author", "Author must not be empty.").isLength({ min: 1 }).trim(),
  body("summary", "Summary must not be empty.").isLength({ min: 1 }).trim(),
  body("isbn", "ISBN must not be empty").isLength({ min: 1 }).trim(),

  body("title").trim().escape(),
  body("author").trim().escape(),
  body("summary").trim().escape(),
  body("isbn").trim().escape(),
  body("genre.*").trim().escape(),
  asyncHandler(async (req, res, next) => {
    const errors = validationResult(req);

    // Create a Book object with escaped/trimmed data and old id.
    var book = new Book({
      title: req.body.title,
      author: req.body.author,
      summary: req.body.summary,
      isbn: req.body.isbn,
      genre: typeof req.body.genre === "undefined" ? [] : req.body.genre,
      _id: req.params.id, //This is required, or a new ID will be assigned!
    });

    if (!errors.isEmpty()) {
      Promise.all([Author.find().exec(), Genre.find().exec()])
        .then(([authors, genres]) => {
          for (let i = 0; i < genres.length; i++) {
            if (book.genre.indexOf(genres[i]._id) > -1) {
              genres[i].checked = "true";
            }
          }
          res.render("book_form", {
            title: "Update Book",
            authors: authors,
            genres: genres,
            book: book,
            errors: errors.array(),
          });
        })
        .catch((err) => {
          if (err) {
            return next(err);
          }
        });
    } else {
      await Book.findByIdAndUpdate(req.params.id, book, {});
      res.redirect(book.url);
    }
  }),
];
